package com.itheima.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.context.BaseContext;
import com.itheima.domain.entity.Info;
import com.itheima.domain.entity.Rate;
import com.itheima.domain.vo.InfoVO;
import com.itheima.mapper.InfoMapper;
import com.itheima.mapper.RateMapper;
import com.itheima.service.RecommendService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static com.itheima.service.impl.InfoServiceImpl.getInfos;


@Service
public class RecommendServiceImpl extends ServiceImpl<RateMapper, Rate> implements RecommendService {
    @Autowired
    private RateMapper rateMapper;
    @Autowired
    private InfoMapper infoMapper;
    public List<InfoVO> recommend() {
        Integer id = Math.toIntExact(BaseContext.getCurrentId());
        Map<Integer, Double> scores = new HashMap<>();
        List<Rate> rates = rateMapper.selectAll();
        Map<Integer, Map<Integer, Integer>> ratings = new HashMap<>();
        for (Rate rate : rates) {
            if(!ratings.containsKey(rate.getUId())){
                ratings.put(rate.getUId(), new HashMap<>());
            }
            else ratings.get(rate.getUId()).put(rate.getJId(), rate.getScore());
        }
        for (Map.Entry<Integer, Map<Integer, Integer>> entry : ratings.entrySet()) {
            Integer uId = entry.getKey();
            if(uId!=id){
                double similarity = cosineSimilarity(ratings.get(id), entry.getValue());
                for (Map.Entry<Integer, Integer> ratingEntry : entry.getValue().entrySet()) {
                    Integer jId = ratingEntry.getKey();
                    if (!ratings.get(id).containsKey(jId)) {
                        scores.put(jId, scores.getOrDefault(jId, 0.0) + similarity * ratingEntry.getValue());
                    }
                }
            }
        }
        List<Integer> collect = scores.entrySet().stream()
                .sorted(Map.Entry.<Integer, Double>comparingByValue().reversed())
                .limit(10)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
        List<Info> records = infoMapper.selectByIds(collect);
        return getInfos(records);
    }



    // 计算余弦相似度
    private static double cosineSimilarity(Map<Integer, Integer> user1, Map<Integer, Integer> user2) {
        Set<Integer> allItems = new HashSet<>(user1.keySet());
        allItems.addAll(user2.keySet());

        double dotProduct = 0.0;
        double norm1 = 0.0;
        double norm2 = 0.0;

        for (Integer item : allItems) {
            int rating1 = user1.getOrDefault(item, 0);
            int rating2 = user2.getOrDefault(item, 0);
            dotProduct += rating1 * rating2;
            norm1 += Math.pow(rating1, 2);
            norm2 += Math.pow(rating2, 2);
        }

        return (norm1 == 0 || norm2 == 0) ? 0 : dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
    }
}
